﻿namespace MakeVSProject4FFmpeg
{
    #region using directives
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Xml;
    #endregion

    internal partial class GenProject
    {
        private ProjectOptions options;

        public GenProject(ProjectOptions options)
        {
            this.options = options;
        }

        private static XmlWriterSettings CreateXmlWriterSettings()
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.Encoding = new UTF8Encoding(false);
            return settings;
        }

        public void WriteProject(IProjInformation information, Stream stream)
        {
            this.WriteProject(information, XmlWriter.Create(stream, CreateXmlWriterSettings()));
        }

        public void WriteProject(IProjInformation information, StringBuilder sb)
        {
            // this will always create XML with UTF-16 encoding...
            this.WriteProject(information, XmlWriter.Create(sb, CreateXmlWriterSettings()));
        }

        public string WriteProjectAsString(IProjInformation information)
        {
            MemoryStream memoryStream = new MemoryStream();
            this.WriteProject(information, memoryStream);
            string xmlString = Encoding.UTF8.GetString(memoryStream.ToArray());
            return xmlString;
        }

        public void WriteProjectFilters(IProjInformation information, Stream stream)
        {
            this.WriteProjectFilters(information, XmlWriter.Create(stream, CreateXmlWriterSettings()));
        }

        private void WriteProjectFilters(IProjInformation information, XmlWriter writer)
        {
            writer.WriteStartElement(ELEMENT_PROJECT, "http://schemas.microsoft.com/developer/msbuild/2003");
            writer.WriteAttributeString("ToolsVersion", "4.0");

            var filters = information.GetFilterFolders();

            string[] srcHdr = new string[] { "Source Files", "Header Files" };

            var filtersSrcHdr = srcHdr.Concat(filters.SelectMany(f => srcHdr, (f, fs) => string.IsNullOrEmpty(f) ? fs : fs + '\\' + f)).ToArray();
            this.WriteFiltersCheckForSubPaths(writer, filtersSrcHdr);

            this.WriteFilterFiles(information.GetFiles(FileType.CFile), ELEMENT_CLCOMPILE, srcHdr[0] + '\\', writer, true);
            /*this.WriteFilterFiles(information.GetFiles(FileType.AsmFile), ELEMENT_YASM, srcHdr[0] + '\\', writer, false);*/
            this.WriteFilterFiles(information.GetFiles(FileType.AsmFile), "CustomBuild", srcHdr[0] + '\\', writer, true);
            this.WriteFilterFiles(information.GetFiles(FileType.HeaderFile), ELEMENT_CLINCLUDE, srcHdr[1] + '\\', writer, true);

            writer.WriteEndElement();   // ELEMENT_PROJECT

            writer.Flush();
            writer.Close();
            writer.Dispose();

        }

        private void WriteFilterFiles(IEnumerable<CompilationUnit> compilationUnits, string compileStartElem, string preFix, XmlWriter writer, bool useBackSlashes)
        {
            writer.WriteStartElement(ELEMENT_ITEMGROUP);
            foreach (var f in compilationUnits)
            {
                writer.WriteStartElement(compileStartElem);
                string fileName = useBackSlashes ? f.GetProjectRelativeFilenameWithBackSlash() : f.GetProjectRelativeFilenameWithForwardSlash();
                writer.WriteAttributeString("Include", fileName);

                writer.WriteStartElement(ELEMENT_FILTER);
                string filter = preFix + f.PathWithoutFileName;
                if (filter.EndsWith("/") || filter.EndsWith("\\"))
                {
                    filter = filter.Substring(0, filter.Length - 1);
                }

                filter = filter.Replace('/', '\\');
                writer.WriteValue(filter);
                writer.WriteEndElement();   //ELEMENT_FILTER
                writer.WriteEndElement();   // ELEMENT_CLCOMPILE
            }

            writer.WriteEndElement();   // ELEMENT_ITEMGROUP
        }

        private void WriteFiltersCheckForSubPaths(XmlWriter writer, IEnumerable<string> filters)
        {
            List<string> filtersNormalized = filters.Select(s => s.Replace('/', '\\')).ToList();

            // in case of a path with several backslashes, we need to include each part -
            // if it does not exists already
            for (int i = 0; i < filtersNormalized.Count; i++)
            {
                if (filtersNormalized[i].Count(f => f == '\\') >= 2)
                {
                    int index = filtersNormalized[i].LastIndexOf(@"\", System.StringComparison.Ordinal);
                    string s = filtersNormalized[i].Substring(0, index);
                    if (!filtersNormalized.Contains(s))
                    {
                        filtersNormalized.Add(s);
                    }
                }
            }

            this.WriteFilters(writer, filtersNormalized);
        }

        private void WriteFilters(XmlWriter writer, IEnumerable<string> filters)
        {
            writer.WriteStartElement(ELEMENT_ITEMGROUP);
            foreach (string f in filters)
            {
                writer.WriteStartElement(ELEMENT_FILTER);
                string filter = f.Replace('/', '\\');
                writer.WriteAttributeString("Include", filter);
                writer.WriteElementString("UniqueIdentifier", Guid.NewGuid().ToString("B"));
                writer.WriteEndElement();   // ELEMENT_FILTER
            }

            writer.WriteEndElement();   // ELEMENT_ITEMGROUP
        }

        private void WriteProject(IProjInformation information, XmlWriter writer)
        {
            writer.WriteStartElement(ELEMENT_PROJECT, "http://schemas.microsoft.com/developer/msbuild/2003");
            writer.WriteAttributeString("DefaultTargets", "Build");
            writer.WriteAttributeString("ToolsVersion", "4.0");
            this.WriteHeaderStuff(writer);

            this.WriteCFiles(information.GetFiles(FileType.CFile), writer);
            this.WriteAsmFiles(information.GetFiles(FileType.AsmFile), writer);
            this.WriteHeaderFiles(information.GetFiles(FileType.HeaderFile), writer);

            this.WriteFooterStuff(writer);
            writer.WriteEndElement();   // ELEMENT_PROJECT

            writer.Flush();
            writer.Close();
            writer.Dispose();
        }

        private void WriteCFiles(IEnumerable<CompilationUnit> compilationUnits, XmlWriter writer)
        {
            writer.WriteStartElement(ELEMENT_ITEMGROUP);
            foreach (CompilationUnit compilationUnit in compilationUnits)
            {
                writer.WriteStartElement(ELEMENT_CLCOMPILE);
                writer.WriteAttributeString("Include", compilationUnit.GetProjectRelativeFilenameWithBackSlash());
                if (compilationUnit.MustTweakObjFileName)
                {
                    this.WriteObjectFileName(writer, this.GetDefaultConditionsEnum()/*DEFAULT_CONDITIONS*/, compilationUnit.GetObjectFilenamePrefix());
                }

                // need to tweak compiler-flags libavcodec/error_resilience.c, otherwise the file is mis-compiled (on x86 in release-build)
                //if (this.options.Platformx86 == true)
                //{
                //    writer.WriteStartElement("AdditionalOptions");
                //    writer.WriteAttributeString("Condition", "'$(Configuration)|$(Platform)'=='Release|Win32'");
                //    writer.WriteValue("-Qvec- -Qsimd- %(AdditionalOptions)");
                //    writer.WriteEndElement();
                //}

                if (!string.IsNullOrEmpty(compilationUnit.AdditionalIncludeDirectories))
                {
                    foreach (string s in this.GetDefaultConditionsEnum())
                    {
                        writer.WriteStartElement("AdditionalIncludeDirectories");
                        writer.WriteAttributeString("Condition", s);
                        writer.WriteValue(compilationUnit.AdditionalIncludeDirectories);
                        writer.WriteEndElement();
                    }
                }

                if (!string.IsNullOrEmpty(compilationUnit.AdditionalPreprocessorDefinitions))
                {
                    foreach (string s in this.GetDefaultConditionsEnum())
                    {
                        writer.WriteStartElement("PreprocessorDefinitions");
                        writer.WriteAttributeString("Condition", s);
                        writer.WriteValue(compilationUnit.AdditionalPreprocessorDefinitions);
                        writer.WriteEndElement();
                    }
                }

                if (compilationUnit.ExcludeFromBuild)
                {
                    writer.WriteStartElement("ExcludedFromBuild");
                    writer.WriteValue(true);
                    writer.WriteEndElement();
                }

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        private IEnumerable<string> GetDefaultConditionsEnum()
        {
            if (this.options.Platformx86 == true)
            {
                yield return "'$(Configuration)|$(Platform)'=='Debug|Win32'";
                yield return "'$(Configuration)|$(Platform)'=='Release|Win32'";
            }

            if (this.options.Platformx64 == true)
            {
                yield return "'$(Configuration)|$(Platform)'=='Debug|x64'";
                yield return "'$(Configuration)|$(Platform)'=='Release|x64'";
            }
        }

        private IEnumerable<string> GetConditionsForPlatformCondition(string platformCondition)
        {
            if (string.CompareOrdinal(platformCondition, "Win32") == 0)
            {
                if (this.options.Platformx86 == true)
                {
                    yield return "'$(Configuration)|$(Platform)'=='Debug|Win32'";
                    yield return "'$(Configuration)|$(Platform)'=='Release|Win32'";
                }
            }

            if (string.CompareOrdinal(platformCondition, "x64") == 0)
            {
                if (this.options.Platformx64 == true)
                {
                    yield return "'$(Configuration)|$(Platform)'=='Debug|x64'";
                    yield return "'$(Configuration)|$(Platform)'=='Release|x64'";
                }
            }
        }

        private static string MakeYasmDefinesCmdLineArgs(string defines)
        {
            string[] definesArray = defines.Split(';');
            StringBuilder sb = new StringBuilder();
            foreach (string s in definesArray)
            {
                if (!string.IsNullOrWhiteSpace(s))
                {
                    sb.Append(" -D");
                    sb.Append(s);
                    sb.Append(" ");
                }
            }

            return sb.ToString();
        }

        private void WriteAsmFilesCustomBuild(IEnumerable<CompilationUnit> compilationUnits, XmlWriter writer)
        {
            writer.WriteStartElement(ELEMENT_ITEMGROUP);
            foreach (CompilationUnit compilationUnit in compilationUnits)
            {
                writer.WriteStartElement("CustomBuild");
                writer.WriteAttributeString("Include", compilationUnit.GetProjectRelativeFilenameWithBackSlash());

                writer.WriteStartElement("FileType");
                writer.WriteValue("Document");
                writer.WriteEndElement();   // FileType

                //HIGH_BIT_DEPTH=0;BIT_DEPTH=8;WIN32=1;PREFIX;HAVE_ALIGNED_STACK=0
                const string Default_Defines_Win32 = "WIN32=1;ARCH_X86_32=1;ARCH_X86_64=0;PREFIX";// +";HIGH_BIT_DEPTH=0;BIT_DEPTH=8;HAVE_ALIGNED_STACK=0";
                const string Default_Defines_Win64 = "ARCH_X86_32=0;ARCH_X86_64=1";


                writer.WriteStartElement("Command");
                writer.WriteAttributeString("Condition", "'$(Configuration)|$(Platform)'=='Debug|Win32'");
                string defines = TryGetOverriddenDefines(compilationUnit, "'$(Configuration)|$(Platform)'=='Debug|Win32'") ?? Default_Defines_Win32;
                writer.WriteValue(string.Format("yasm.exe -m x86 -f win32 -X vc -g cv8 -I$(ProjectDir) {0} -Pconfig.asm -o \"$(IntDir)\\%(Filename).obj\" \"%(FullPath)\"", MakeYasmDefinesCmdLineArgs(defines)));
                writer.WriteEndElement();
                writer.WriteStartElement("Command");
                writer.WriteAttributeString("Condition", "'$(Configuration)|$(Platform)'=='Release|Win32'");
                defines = TryGetOverriddenDefines(compilationUnit, "'$(Configuration)|$(Platform)'=='Release|Win32'") ?? Default_Defines_Win32;
                writer.WriteValue(string.Format("yasm.exe -m x86 -f win32 -X vc  -g cv8 -I$(ProjectDir) {0} -Pconfig.asm -o \"$(IntDir)\\%(Filename).obj\" \"%(FullPath)\"", MakeYasmDefinesCmdLineArgs(defines)));
                writer.WriteEndElement();
                writer.WriteStartElement("Command");
                writer.WriteAttributeString("Condition", "'$(Configuration)|$(Platform)'=='Debug|x64'");
                defines = TryGetOverriddenDefines(compilationUnit, "'$(Configuration)|$(Platform)'=='Debug|x64'") ?? Default_Defines_Win64;
                writer.WriteValue(string.Format("yasm.exe -m amd64 -f win64 -X vc  -g cv8 -I$(ProjectDir) {0} -Pconfig.asm -o \"$(IntDir)\\%(Filename).obj\" \"%(FullPath)\"", MakeYasmDefinesCmdLineArgs(defines)));
                writer.WriteEndElement();
                writer.WriteStartElement("Command");
                writer.WriteAttributeString("Condition", "'$(Configuration)|$(Platform)'=='Release|x64'");
                defines = TryGetOverriddenDefines(compilationUnit, "'$(Configuration)|$(Platform)'=='Release|x64'") ?? Default_Defines_Win64;
                writer.WriteValue(string.Format("yasm.exe -m amd64 -f win64 -X vc  -g cv8 -I$(ProjectDir) {0} -Pconfig.asm -o \"$(IntDir)\\%(Filename).obj\" \"%(FullPath)\"", MakeYasmDefinesCmdLineArgs(defines)));
                writer.WriteEndElement();   // Command

                foreach (string condition in this.GetDefaultConditionsEnum())
                {
                    writer.WriteStartElement("Outputs");
                    writer.WriteAttributeString("Condition", condition);
                    writer.WriteValue(@"$(IntDir)\%(Filename).obj");
                    writer.WriteEndElement();   // Outputs
                }

                foreach (string condition in this.GetDefaultConditionsEnum())
                {
                    writer.WriteStartElement("Message");
                    writer.WriteAttributeString("Condition", condition);
                    writer.WriteValue(@"YASM: assembling %(Filename)%(Extension)");
                    writer.WriteEndElement();   // Outputs
                }

                if (compilationUnit.ExcludeFromBuild)
                {
                    writer.WriteStartElement("ExcludedFromBuild");
                    writer.WriteValue(true);
                    writer.WriteEndElement();
                }
                else
                {
                    if (compilationUnit.HasExcludeFromBuildWithCondition)
                    {
                        foreach (string platformCondition in compilationUnit.ExcludeFromBuildWithCondition)
                        {
                            foreach (string s in GetConditionsForPlatformCondition(platformCondition))
                            {
                                writer.WriteStartElement("ExcludedFromBuild");
                                writer.WriteAttributeString("Condition", s);
                                writer.WriteValue(true);
                                writer.WriteEndElement();
                            }
                        }
                    }
                }

                writer.WriteEndElement();   // CustomBuild
            }

            writer.WriteEndElement();
        }

        private static string TryGetOverriddenDefines(CompilationUnit unit, string condition)
        {
            if (unit.HasNodeConditionValueList)
            {
                var con = unit.NodeConditionValueList.FirstOrDefault(x => string.CompareOrdinal(x.Item1, "Defines") == 0 && string.CompareOrdinal(x.Item2, condition) == 0);
                if (con != null)
                {
                    return con.Item3;
                }
            }

            return null;
        }

        private void WriteAsmFiles(IEnumerable<CompilationUnit> compilationUnits, XmlWriter writer)
        {
            WriteAsmFilesCustomBuild(compilationUnits, writer);
            /*
            writer.WriteStartElement(ELEMENT_ITEMGROUP);
            foreach (CompilationUnit compilationUnit in compilationUnits)
            {
                writer.WriteStartElement(ELEMENT_YASM);
                writer.WriteAttributeString("Include", compilationUnit.GetProjectRelativeFilename());

                if (compilationUnit.ExcludeFromBuild)
                {
                    writer.WriteStartElement("ExcludedFromBuild");
                    writer.WriteValue(true);
                    writer.WriteEndElement();
                }
                else
                {
                    if (compilationUnit.HasExcludeFromBuildWithCondition)
                    {
                        foreach (string platformCondition in compilationUnit.ExcludeFromBuildWithCondition)
                        {
                            foreach (string s in GetConditionsForPlatformCondition(platformCondition))
                            {
                                writer.WriteStartElement("ExcludedFromBuild");
                                writer.WriteAttributeString("Condition", s);
                                writer.WriteValue(true);
                                writer.WriteEndElement();
                            }
                        }
                    }
                }

                if (compilationUnit.HasNodeConditionValueList)
                {
                    foreach (var tuple in compilationUnit.NodeConditionValueList)
                    {
                        writer.WriteStartElement(tuple.Item1);
                        writer.WriteAttributeString("Condition", tuple.Item2);
                        writer.WriteValue(tuple.Item3);
                        writer.WriteEndElement();
                    }
                }

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
            */
        }

        private void WriteHeaderFiles(IEnumerable<CompilationUnit> compilationUnits, XmlWriter writer)
        {
            writer.WriteStartElement(ELEMENT_ITEMGROUP);
            foreach (CompilationUnit compilationUnit in compilationUnits)
            {
                writer.WriteStartElement(ELEMENT_CLINCLUDE);
                writer.WriteAttributeString("Include", compilationUnit.GetProjectRelativeFilenameWithBackSlash());
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        private void WriteAdditionalIncludeDirectories(XmlWriter writer, IEnumerable<string> conditions)
        {
            foreach (string condition in conditions)
            {
                writer.WriteStartElement(ELEMENT_ADDITIONALINCLUDEDIRECTORIES);
                writer.WriteAttributeString("Condition", condition);
                writer.WriteValue(@"$(ProjectDir);$(ProjectDir)\compat\msvcrt;%(AdditionalIncludeDirectories)");
                writer.WriteEndElement();
            }
        }

        private void WriteObjectFileName(XmlWriter writer, IEnumerable<string> conditions, string objFilenamePrefix)
        {
            const string ELEMENT_OBJECTFILENAME = "ObjectFileName";
            string alternativeFilename = string.Format("$(IntDir){0}%(Filename)", objFilenamePrefix);
            foreach (string condition in conditions)
            {
                writer.WriteStartElement(ELEMENT_OBJECTFILENAME);
                writer.WriteAttributeString("Condition", condition);
                writer.WriteValue(alternativeFilename);
                writer.WriteEndElement();

            }
        }

        private string GetPlatformToolset()
        {
            return "Intel C++ Compiler XE 13.0";    // "v110"
        }

        private void WriteProjectConfiguration(XmlWriter writer, string configuration, string platform)
        {
            const string ELEMENT_PROJECTCONFIGURATION = "ProjectConfiguration";
            const string ELEMENT_CONFIGURATION = "Configuration";
            const string ELEMENT_PLATFORM = "Platform";
            writer.WriteStartElement(ELEMENT_PROJECTCONFIGURATION);
            writer.WriteAttributeString("Include", string.Format("{0}|{1}", configuration, platform));
            writer.WriteElementString(ELEMENT_CONFIGURATION, configuration);
            writer.WriteElementString(ELEMENT_PLATFORM, platform);
            writer.WriteEndElement();       // ELEMENT_PROJECTCONFIGURATION
        }

        private void WritePropertyGroup_Label_Configuration(XmlWriter writer, string condition, bool useDebugLibraries, bool? wholeProgramOptimization)
        {
            writer.WriteStartElement(ELEMENT_PROPERTYGROUP);
            writer.WriteAttributeString("Condition", string.Format("'$(Configuration)|$(Platform)'=='{0}'", condition));
            writer.WriteAttributeString("Label", "Configuration");
            writer.WriteElementString("ConfigurationType", "Application");
            writer.WriteStartElement("UseDebugLibraries");
            writer.WriteValue(useDebugLibraries);
            writer.WriteEndElement();
            writer.WriteElementString("PlatformToolset", this.GetPlatformToolset());
            if (wholeProgramOptimization.HasValue)
            {
                writer.WriteStartElement("WholeProgramOptimization");
                writer.WriteValue(wholeProgramOptimization.Value);
                writer.WriteEndElement();
            }

            writer.WriteElementString("CharacterSet", "Unicode");
            writer.WriteEndElement();       // ELEMENT_PROPERTYGROUP
        }

        private void WritePropertyGroup_Label_PropertySheets(XmlWriter writer, string condition)
        {
            writer.WriteStartElement(ELEMENT_IMPORTGROUP);
            writer.WriteAttributeString("Label", "PropertySheets");
            writer.WriteAttributeString("Condition", string.Format("'$(Configuration)|$(Platform)'=='{0}'", condition));
            writer.WriteStartElement(ELEMENT_IMPORT);
            writer.WriteAttributeString("Project", "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props");
            writer.WriteAttributeString("Condition", "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')");
            writer.WriteAttributeString("Label", "LocalAppDataPlatform");
            writer.WriteEndElement();       // ELEMENT_IMPORT
            writer.WriteEndElement();       // ELEMENT_IMPORTGROUP
        }

        private void WritePropertyGroup_OutDirIntDir(XmlWriter writer, string condition, bool linkIncremental)
        {
            writer.WriteStartElement(ELEMENT_PROPERTYGROUP);
            writer.WriteAttributeString("Condition", string.Format("'$(Configuration)|$(Platform)'=='{0}'", condition));
            writer.WriteStartElement("LinkIncremental");
            writer.WriteValue(linkIncremental);
            writer.WriteEndElement();
            writer.WriteElementString("OutDir", @"$(SolutionDir)$(Platform)\$(Configuration)\");
            writer.WriteElementString("IntDir", @"$(Platform)\$(Configuration)\");
            writer.WriteEndElement();       // ELEMENT_PROPERTYGROUP
        }

        private void WriterItemDefinitionGroup_ClCompile_Link_Yasm(
                                                                XmlWriter writer,
                                                                string condition,
                                                                string optimization,
                                                                string preprocessorDefinitions,
                                                                string forcedIncludeFiles,
                                                                bool? exceptionHandling,
                                                                string additionalIncludeDirectories,
                                                                bool generateDebugInfo,
                                                                string additionalDependencies,
                                                                bool? enableCOMDATFolding,
                                                                bool? optimizeReferences,
                                                                string yasmDefines,
                                                                string yasmAdditionalOptions,
                                                                bool? multiProcessorCompilation,
                                                                string basicRuntimeChecks)
        {
            writer.WriteStartElement(ELEMENT_ITEMDEFINITIONGROUP);
            writer.WriteAttributeString("Condition", string.Format("'$(Configuration)|$(Platform)'=='{0}'", condition));
            writer.WriteStartElement(ELEMENT_CLCOMPILE);
            writer.WriteElementString("PrecompiledHeader", "NotUsing");
            writer.WriteElementString("WarningLevel", "Level3");
            writer.WriteElementString("Optimization", optimization);
            writer.WriteElementString("FunctionLevelLinking", "true");

            writer.WriteElementString(ELEMENT_C99SUPPORT, "true");
            writer.WriteElementString("PreprocessorDefinitions", preprocessorDefinitions);
            writer.WriteElementString("ForcedIncludeFiles", forcedIncludeFiles);
            if (exceptionHandling.HasValue)
            {
                writer.WriteStartElement("ExceptionHandling");
                writer.WriteValue(exceptionHandling.Value);
                writer.WriteEndElement();
            }

            writer.WriteElementString("AdditionalIncludeDirectories", additionalIncludeDirectories);
            if (multiProcessorCompilation.HasValue)
            {
                writer.WriteStartElement("MultiProcessorCompilation");
                writer.WriteValue(multiProcessorCompilation.Value);
                writer.WriteEndElement();
            }

            if (basicRuntimeChecks != null)
            {
                writer.WriteStartElement("BasicRuntimeChecks");
                writer.WriteValue(basicRuntimeChecks);
                writer.WriteEndElement();
            }

            writer.WriteEndElement();       // ELEMENT_CLCOMPILE 
            writer.WriteStartElement(ELEMENT_LINK);
            writer.WriteElementString("SubSystem", "Console");
            writer.WriteElementString("GenerateDebugInformation", generateDebugInfo == true ? "true" : "false");
            if (enableCOMDATFolding.HasValue)
            {
                writer.WriteStartElement("EnableCOMDATFolding");
                writer.WriteValue(enableCOMDATFolding.Value);
                writer.WriteEndElement();
            }

            if (optimizeReferences.HasValue)
            {
                writer.WriteStartElement("OptimizeReferences");
                writer.WriteValue(optimizeReferences.Value);
                writer.WriteEndElement();

            }

            writer.WriteElementString("AdditionalDependencies", additionalDependencies);
            writer.WriteEndElement();       // ELEMENT_LINK
            writer.WriteStartElement(ELEMENT_YASM);
            writer.WriteElementString("IncludePaths", "$(ProjectDir)");
            writer.WriteElementString("PreIncludeFile", "config.asm");
            writer.WriteElementString("Defines", yasmDefines);
            if (!string.IsNullOrEmpty(yasmAdditionalOptions))
            {
                writer.WriteElementString("AdditionalOptions", yasmAdditionalOptions);
            }

            writer.WriteEndElement();       // ELEMENT_YASM
            writer.WriteEndElement();       // ELEMENT_ITEMDEFINITIONGROUP 
        }

        private void WriteHeaderStuff(XmlWriter writer)
        {
            writer.WriteStartElement(ELEMENT_ITEMGROUP);
            writer.WriteAttributeString("Label", "ProjectConfigurations");

            if (this.options.Platformx86 == true)
            {
                this.WriteProjectConfiguration(writer, "Debug", "Win32");
                this.WriteProjectConfiguration(writer, "Release", "Win32");
            }

            if (this.options.Platformx64 == true)
            {
                this.WriteProjectConfiguration(writer, "Debug", "x64");
                this.WriteProjectConfiguration(writer, "Release", "x64");
            }

            writer.WriteEndElement();       // ELEMENT_ITEMGROUP


            writer.WriteStartElement(ELEMENT_PROPERTYGROUP);
            writer.WriteAttributeString("Label", "Globals");
            writer.WriteElementString("ProjectGuid", this.options.ProjectGuid.ToString("B"));
            writer.WriteElementString("Keyword", "Win32Proj");
            writer.WriteElementString("RootNamespace", this.options.ProjectName);
            writer.WriteEndElement();       // ELEMENT_PROPERTYGROUP

            writer.WriteStartElement(ELEMENT_IMPORT);
            writer.WriteAttributeString("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props");
            writer.WriteEndElement();       // ELEMENT_IMPORT

            if (this.options.Platformx86 == true)
            {
                this.WritePropertyGroup_Label_Configuration(writer, "Debug|Win32", true, null);
                this.WritePropertyGroup_Label_Configuration(writer, "Release|Win32", false, false);
            }

            if (this.options.Platformx64 == true)
            {
                this.WritePropertyGroup_Label_Configuration(writer, "Debug|x64", true, null);
                this.WritePropertyGroup_Label_Configuration(writer, "Release|x64", false, false);
            }

            writer.WriteStartElement(ELEMENT_IMPORT);
            writer.WriteAttributeString("Project", @"$(VCTargetsPath)\Microsoft.Cpp.props");
            writer.WriteEndElement();       // ELEMENT_IMPORT

            writer.WriteStartElement(ELEMENT_IMPORTGROUP);
            writer.WriteAttributeString("Label", "ExtensionSettings");
            writer.WriteStartElement(ELEMENT_IMPORT);
            writer.WriteAttributeString("Project", @"$(VCTargetsPath)\BuildCustomizations\vsyasm.props");
            writer.WriteEndElement();       // ELEMENT_IMPORT
            writer.WriteEndElement();       // ELEMENT_IMPORTGROUP

            if (this.options.Platformx86 == true)
            {
                this.WritePropertyGroup_Label_PropertySheets(writer, "Debug|Win32");
                this.WritePropertyGroup_Label_PropertySheets(writer, "Release|Win32");
            }

            if (this.options.Platformx64 == true)
            {
                this.WritePropertyGroup_Label_PropertySheets(writer, "Debug|x64");
                this.WritePropertyGroup_Label_PropertySheets(writer, "Release|x64");
            }

            writer.WriteStartElement(ELEMENT_PROPERTYGROUP);
            writer.WriteAttributeString("Label", "UserMacros");
            writer.WriteEndElement();       // ELEMENT_PROPERTYGROUP

            if (this.options.Platformx86 == true)
            {
                this.WritePropertyGroup_OutDirIntDir(writer, "Debug|Win32", true);
                this.WritePropertyGroup_OutDirIntDir(writer, "Release|Win32", false);
            }

            if (this.options.Platformx64 == true)
            {
                this.WritePropertyGroup_OutDirIntDir(writer, "Debug|x64", true);
                this.WritePropertyGroup_OutDirIntDir(writer, "Release|x64", false);
            }

            if (this.options.Platformx86 == true)
            {
                this.WriterItemDefinitionGroup_ClCompile_Link_Yasm(
                    writer,
                    "Debug|Win32",
                    "Disabled",
                    "_DEBUG;" + PREPROCESSORDEFINITIONS,
                    FORCEDINCLUDEFILES,
                    false,
                    @"$(ProjectDir);$(ProjectDir)\compat\msvcrt",
                    true,
                    ADDITIONALDEPENDENCIES,
                    null,
                    null,
                    "WIN32=1;ARCH_X86_32=1;ARCH_X86_64=0;PREFIX",
                    string.Empty,
                    true,
                    "StackFrameRuntimeCheck");
                this.WriterItemDefinitionGroup_ClCompile_Link_Yasm(
                    writer,
                    "Release|Win32",
                    "MaxSpeed",
                    "NDEBUG;" + PREPROCESSORDEFINITIONS,
                    FORCEDINCLUDEFILES,
                    false,
                    @"$(ProjectDir);$(ProjectDir)\compat\msvcrt",
                    true,
                    ADDITIONALDEPENDENCIES,
                    true,
                    true,
                    "WIN32=1;ARCH_X86_32=1;ARCH_X86_64=0;PREFIX",
                    string.Empty,
                    true,
                    null);
            }

            if (this.options.Platformx64 == true)
            {
                this.WriterItemDefinitionGroup_ClCompile_Link_Yasm(
                    writer,
                    "Debug|x64",
                    "Disabled",
                    "_DEBUG;" + PREPROCESSORDEFINITIONS,
                    FORCEDINCLUDEFILES,
                    false,
                    @"$(ProjectDir);$(ProjectDir)\compat\msvcrt",
                    true,
                    ADDITIONALDEPENDENCIES,
                    null,
                    null,
                    "ARCH_X86_32=0;ARCH_X86_64=1",
                    "-f win64 -m amd64 %(AdditionalOptions)",
                    true,
                    "StackFrameRuntimeCheck");
                this.WriterItemDefinitionGroup_ClCompile_Link_Yasm(
                    writer,
                    "Release|x64",
                    "MaxSpeed",
                    "NDEBUG;" + PREPROCESSORDEFINITIONS,
                    FORCEDINCLUDEFILES,
                    false,
                    @"$(ProjectDir);$(ProjectDir)\compat\msvcrt",
                    true,
                    ADDITIONALDEPENDENCIES,
                    true,
                    true,
                    "ARCH_X86_32=0;ARCH_X86_64=1",
                    "-f win64 -m amd64 %(AdditionalOptions)",
                    true,
                    null);
            }
        }

        private void WriteFooterStuff(XmlWriter writer)
        {
            writer.WriteStartElement(ELEMENT_IMPORT);
            writer.WriteAttributeString("Project", @"$(VCTargetsPath)\Microsoft.Cpp.targets");
            writer.WriteEndElement();       // ELEMENT_IMPORT

            writer.WriteStartElement(ELEMENT_IMPORTGROUP);
            writer.WriteAttributeString("Label", "ExtensionTargets");
            writer.WriteStartElement(ELEMENT_IMPORT);
            writer.WriteAttributeString("Project", @"$(VCTargetsPath)\BuildCustomizations\vsyasm.targets");
            writer.WriteEndElement();       // ELEMENT_IMPORT
            writer.WriteEndElement();       // ELEMENT_IMPORTGROUP
        }
    }

    internal partial class GenProject
    {
        private const string ELEMENT_ITEMGROUP = "ItemGroup";

        private const string ELEMENT_IMPORTGROUP = "ImportGroup";

        private const string ELEMENT_IMPORT = "Import";

        private const string ELEMENT_ITEMDEFINITIONGROUP = "ItemDefinitionGroup";

        private const string ELEMENT_CLCOMPILE = "ClCompile";

        private const string ELEMENT_CLINCLUDE = "ClInclude";

        private const string ELEMENT_LINK = "Link";

        private const string ELEMENT_YASM = "YASM";

        private const string ELEMENT_PROJECT = "Project";

        private const string ELEMENT_FILTER = "Filter";

        private const string ELEMENT_ADDITIONALINCLUDEDIRECTORIES = "AdditionalIncludeDirectories";

        private const string ELEMENT_C99SUPPORT = "C99Support";

        private const string ELEMENT_PROPERTYGROUP = "PropertyGroup";

        private const string PREPROCESSORDEFINITIONS =
            "WIN32;_CONSOLE;_ISOC99_SOURCE;_WIN32_WINNT=0x0502;_FILE_OFFSET_BITS=64;_LARGEFILE_SOURCE;_USE_MATH_DEFINES;inline=__inline;strtoll=_strtoi64;HAVE_AV_CONFIG_H;%(PreprocessorDefinitions)";

        private const string FORCEDINCLUDEFILES = @"mathimf.h;stdlib.h;compat\msvcrt\snprintf.h;compat\strtod.h%(ForcedIncludeFiles)";

        private const string ADDITIONALDEPENDENCIES = @"Ws2_32.lib;Vfw32.lib;Strmiids.lib;Psapi.lib;%(AdditionalDependencies)";
    }
}
